import os, sys
try:
    import numpy as np
    import cv2
except:
    os.system("pip3 install numpy opencv-python")
    import numpy as np
    import cv2

 
def d_save():
    cv2.imencode('.'+os.path.basename(main.a1).split('.')[-1], main.img_show)[1].tofile('save.'+os.path.basename(main.a1))


def main_crop_ui(event, x, y, flags, param):

    if event == cv2.EVENT_LBUTTONDOWN:
        main.is_LBUTTONDOWN = True
        if main.is_RBUTTONDOWN == True:
            main.is_skip_LBUTTONDOWN = True
        if main.is_RBUTTONDOWN == False and main.is_skip_LBUTTONDOWN == False:
            main.q1_x.append(x)
            main.q2_y.append(y)
            main.img_show = main.img.copy()
            cv2.rectangle(main.img_show, (min(main.q1_x), min(main.q2_y)), (max(main.q1_x), max(main.q2_y)), (255, 0, 0), 1)
            cv2.imshow("image", main.img_show)
    
    if event == cv2.EVENT_MOUSEMOVE and main.is_LBUTTONDOWN == True:
        main.img_show = main.img.copy()
        cv2.rectangle(main.img_show, (min(main.q1_x+[x]), min(main.q2_y+[y])), (max(main.q1_x+[x]), max(main.q2_y+[y])), (255, 0, 0), 1)
        cv2.imshow("image", main.img_show)

    if event == cv2.EVENT_LBUTTONUP:
        main.is_LBUTTONDOWN = False
        if main.is_skip_LBUTTONDOWN == False:
            main.q1_x.append(x)
            main.q2_y.append(y)
            main.img_show = main.img.copy()
            cv2.rectangle(main.img_show, (min(main.q1_x), min(main.q2_y)), (max(main.q1_x), max(main.q2_y)), (255, 0, 0), 10)
            cv2.imshow("image", main.img_show)
        if main.is_skip_LBUTTONDOWN:
            main.is_skip_LBUTTONDOWN = False

    if event == cv2.EVENT_RBUTTONDOWN:
        main.is_RBUTTONDOWN = True
        if min(main.q2_y) < 0: main.v_crop_1 = 0
        else: main.v_crop_1 = min(main.q2_y)
        if max(main.q2_y) > main.v_h: main.v_crop_2 = main.v_h
        else: main.v_crop_2 = max(main.q2_y)
        if min(main.q1_x) < 0: main.v_crop_3 = 0
        else: main.v_crop_3 = min(main.q1_x)
        if max(main.q1_x) > main.v_w: main.v_crop_4 = main.v_w
        else: main.v_crop_4 = max(main.q1_x)
        main.img_show = main.img[main.v_crop_1:main.v_crop_2, main.v_crop_3:main.v_crop_4]
        
        if not main.is_noRatio:
            v1 = main.v_crop_4-main.v_crop_3
            v2 = main.v_crop_2-main.v_crop_1
            v = (main.v_crop_y*v1/main.v_crop_x-v2)//2
            v_broader_topBottom = (v + abs(v))//2
            v = (main.v_crop_x*v2/main.v_crop_y-v1)//2
            v_broader_leftRight = (v + abs(v))//2
            # copyMakeBorder(img, int top, int bottom, int left, int right...
            main.img_show = cv2.copyMakeBorder(main.img_show, int(v_broader_topBottom), int(v_broader_topBottom), int(v_broader_leftRight), int(v_broader_leftRight), cv2.BORDER_REPLICATE)
        
        cv2.imshow("image", main.img_show)

    if event == cv2.EVENT_RBUTTONUP:
        main.is_RBUTTONDOWN = False
        if main.is_LBUTTONDOWN == False:
            d_save()
            main.is_destroyAllWindows = True
        if main.is_LBUTTONDOWN == True:
            main.img_show = main.img
            main.q1_x = []
            main.q2_y = []
            cv2.imshow("image", main.img_show)


def main_crop_resize(a1, a2):
    # To support treading, use img, not main.img
    img = cv2.imdecode(np.fromfile(a1, dtype=np.uint8), -1)

    h, w, d= img.shape
    v3 = list(map(int, a2.split(':')))
    v1 = v3[1]*w - v3[0]*h
    
    if v1 > 0:
        # w >> h
        v4 = int(v1/2/v3[1])
        img = img[0:h, v4:(w-v4)]
    else:
        v4 = int(v1/2/v3[0])
        img = img[v4:(h-v4), 0:w]
    cv2.imencode('.'+os.path.basename(a1).split('.')[-1], img)[1].tofile('save.'+os.path.basename(a1))


def main(a1, a2):
    main.a1 = a1
    main.q1_x = []
    main.q2_y = []
    main.is_LBUTTONDOWN = False
    main.is_RBUTTONDOWN = False
    main.is_skip_LBUTTONDOWN = False
    if not main.is_noRatio:
        main.v_crop_x = int(a2.split(':')[0])
        main.v_crop_y = int(a2.split(':')[1])
    main.img = cv2.imdecode(np.fromfile(main.a1, dtype=np.uint8), -1)
    # main.img = cv2.imread(main.a1)
    main.v_h, main.v_w, d = main.img.shape
    main.img_show = main.img.copy()
    cv2.namedWindow("image", cv2.WINDOW_NORMAL)
    cv2.setMouseCallback("image", main_crop_ui)

    main.is_destroyAllWindows = False
    while True:
        cv2.imshow("image", main.img_show)
        key = cv2.waitKey(1) & 0xFF
        if main.is_destroyAllWindows == True:
            # cv2.destroyAllWindows()
            break


def main_dir(a1, a2):
    # :dir:file:
    for i in os.listdir(a1):
        if i[-4:] in ['.jpg', 'jpeg', '.png', '.bmp']:
            # print(os.path.join(a1, i))
            main(os.path.join(a1, i), a2)


def main_sameSize(a1, a2=None):
    q1 = []

    if not os.path.isdir(a1):
        if a1[-4:] in ['.jpg', 'jpeg', '.png', '.bmp']:
            q1.append(a1)
    else:
        for i1 in os.listdir(a1):
            if i1[-4:] in ['.jpg', 'jpeg', '.png', '.bmp']:
                q1.append(os.path.join(a1, i1))

    if main.is_resize:
        import threading
        threads = []
        if sys.maxsize>2**32:
            for i3 in q1[1:]:
                threads.append(threading.Thread(target=main_crop_resize, args=(i3, a2)))
            for t in threads:
                t.start()

        else:
            for i4 in q1[1:]:
                main_crop_resize(i4, a2)

    else:
        main(q1[0], a2)
        for i2 in q1[1:]:
            main.img = cv2.imdecode(np.fromfile(i2, dtype=np.uint8), -1)
            main.img_show = main.img[main.v_crop_1:main.v_crop_2, main.v_crop_3:main.v_crop_4]
            
            if main.is_noRatio:
                main.img_show = main.img_show
            else:
                v1 = main.v_crop_4-main.v_crop_3
                v2 = main.v_crop_2-main.v_crop_1
                v = (main.v_crop_y*v1/main.v_crop_x-v2)//2
                v_broader_topBottom = (v + abs(v))//2
                v = (main.v_crop_x*v2/main.v_crop_y-v1)//2
                v_broader_leftRight = (v + abs(v))//2
                main.img_show = cv2.copyMakeBorder(main.img_show, int(v_broader_topBottom), int(v_broader_topBottom), int(v_broader_leftRight), int(v_broader_leftRight), cv2.BORDER_REPLICATE)
            cv2.imencode('.'+os.path.basename(i2).split('.')[-1], main.img_show)[1].tofile('save.'+os.path.basename(i2))
                


if __name__ == "__main__":
    if not len(sys.argv) <3:
        if "-r" in sys.argv[1:] or "--resize" in sys.argv[1:]:
            if not os.path.exists(sys.argv[1]):
                print("\"%s\" is unreachable" %sys.argv[1])
                sys.exit(0)
            main.is_resize = True
            main.is_noRatio = False
            main_sameSize(sys.argv[1], sys.argv[2])
            sys.exit(0)

        else:
            main.is_resize = False

            if "-n" in sys.argv[1:] or "--noratio" in sys.argv[1:]:
                main.is_noRatio = True
            else: main.is_noRatio = False
            
        if os.path.isfile(sys.argv[1]):
            main(sys.argv[1], sys.argv[2])
        elif os.path.isdir(sys.argv[1]):
            if "-s" in sys.argv[1:] or "--same" in sys.argv[1:]:
                main.is_sameSize = True
                main_sameSize(sys.argv[1], sys.argv[2])
                sys.exit(0)
            else:
                main.is_sameSize = False

            main_dir(sys.argv[1], sys.argv[2])
        else:
            print("\"%s\" is unreachable" %sys.argv[1])



    else:
        print()
        print("\tTo crop pictures, Use the tool via:")
        print()
        print("\tpython [*this] [*path|dir*] [*int*]:[*int*] ...")
        print()
        print("\t-s   --same\tCrop into same size, \"--noratio\" is optional.")
        print("\t-r   --resize\tResize pic by cropping, a ratio is needed.")
        print("\t-n   --noratio\tCrop without ratio")
        print()
